package mn.idax.exchange.otc.service.impl;

import com.alibaba.fastjson.JSON;
import mn.idax.exchange.common.bean.BaseResponse;
import mn.idax.exchange.otc.constant.CommonConstant;
import mn.idax.exchange.otc.constant.ReferenceExchageConstant;
import mn.idax.exchange.otc.domain.*;
import mn.idax.exchange.otc.domain.EmaiDto.BaseEmaiDTO;
import mn.idax.exchange.otc.domain.EmaiDto.DownOrderDTO;
import mn.idax.exchange.otc.domain.EmaiDto.ReleaseConinDTO;
import mn.idax.exchange.otc.domain.capital.CapitalAPIParamsDTO;
import mn.idax.exchange.otc.domain.capital.ChangeAccountDTO;
import mn.idax.exchange.otc.domain.capital.ChangeListData;
import mn.idax.exchange.otc.domain.capital.RequestLogSimple;
import mn.idax.exchange.otc.entity.*;
import mn.idax.exchange.otc.enums.*;
import mn.idax.exchange.otc.enums.response.OTCAdResponseEnum;
import mn.idax.exchange.otc.enums.response.OrderResponseEnum;
import mn.idax.exchange.otc.exception.IdaxException;
import mn.idax.exchange.otc.mapper.OTCOrderMapper;
import mn.idax.exchange.otc.mapper.OTCUserGroupMapper;
import mn.idax.exchange.otc.mapper.OTCUserMapper;
import mn.idax.exchange.otc.service.*;
import mn.idax.exchange.otc.util.*;
import mn.idax.exchange.otc.util.function.OrderProcessLogFunction;
import mn.idax.exchange.otc.vo.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import static java.util.Optional.ofNullable;

/**
 * Create by jack_huang on 2018/8/6
 */

//@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@SuppressWarnings("ALL")
@Service
public class OTCOrderServiceImpl extends ReferenceExchageConstant implements OTCOrderService {

    private static ExecutorService executorService = Executors.newFixedThreadPool(1);

    @Autowired
    SqsService sqsService;
    @Autowired
    private OTCOrderMapper otcOrderMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private OTCAdsService otcAdsService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private UserService userService;
    @Autowired
    private OTCUserService otcUserService;
    @Autowired
    private OTCOrderLogService otcOrderLogService;
    @Autowired
    private CoinService coinService;
    @Autowired
    private AccountTransactionService transactionService;
    @Autowired
    private OTCBeneficiarysService otcBeneficiarysService;
    @Autowired
    private OTCSysDictionaryItemService otcSysDictionaryItemService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private OTCCommentService otcCommentService;
    @Autowired
    private OTCUserGroupService otcUserGroupService;
    @Autowired
    private OTCUserGroupMapper otcUserGroupMapper;
    @Autowired
    private AccountCapitalService accountCapitalService;
    @Autowired
    private OTCOrderService otcOrderService;
    @Autowired
    private OTCUserMapper otcUserMapper;
    @Autowired
    private PairUtils pairUtils;
    @Autowired
    private UserLanguageService userLanguageService;
    @Autowired
    private OTCArbitrationService otcArbitrationService;

    @Value("${sqs.queue.name}")
    private String sqsQueueName;

    @Value("${data.mongodb.uri}")
    private String monggoURI;

    @Value("${modulus.orderTotal}")
    private BigDecimal orderTotal;

    @Value("${modulus.orderTradeRate}")
    private BigDecimal orderTradeRate;

    @Value("${modulus.orderTotalQty}")
    private BigDecimal orderTotalQty;

    @Value("${trade.coin.czzLastPrice}")
    private BigDecimal czzLastPrice;

    @Autowired
    private ChatLogServiceImpl chatLogService;

    @Autowired
    private JiGuangService jiGuangService;

    @Autowired
    private CapitalAPIBaseService capitalAPIBaseService;

    @Autowired
    private OTCOrderLogService orderLogService;

    @Autowired
    private FileUrlService fileUrlService;

    public static Date expireTime(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MINUTE, CommonConstant.PAYTIMEOUT);
        Date expireTime = calendar.getTime();
//        long current = date.getTime();
//        current += CommonConstant.PAYTIMEOUT * 60 * 1000;
//        Date expireTime = new Date(current);
        return expireTime;
    }

    //通过订单号查询订单
    @Override
    public OTCOrder getOrderByOrderNum(String orderNum) {
        OTCOrderExample example = new OTCOrderExample();
        example.createCriteria().andNumberEqualTo(orderNum);
        List<OTCOrder> otcOrders = otcOrderMapper.selectByExampleWithBLOBs(example);
        return otcOrders.isEmpty() ? null : otcOrders.get(0);
    }

    //订单列表展示
    @Override
    public OrderShowResultDTO getOrderPage(OrderPageDTO orderPageDTO) {
        List<OrderResultDTO> otcOrders = otcOrderMapper.getOrderList(orderPageDTO);
        if (otcOrders.isEmpty()) {
            List<OrderPageListDTO> orderPageListDTOS = new ArrayList<>();
            return new OrderShowResultDTO(orderPageListDTOS, 0);
        }
        List<OrderPageListDTO> orders = getOrderResultList(otcOrders, orderPageDTO.getUserId());
        Integer total = getOrderTotal(orderPageDTO);

        return new OrderShowResultDTO(orders, total);
    }

    @Override
    public List<OrderExcelDTO> queryExportData(OrderPageDTO orderPageDTO) {
        return this.otcOrderMapper.listExportExcel(orderPageDTO);
    }

    //订单列表total
    private Integer getOrderTotal(OrderPageDTO orderPageDTO) {
//        Integer sellTotal = otcOrderMapper.getSellOrderTotal(orderPageDTO);
//        Integer buyTotal = otcOrderMapper.getBuyOrderTotal(orderPageDTO);
//        return sellTotal+buyTotal;
        return otcOrderMapper.getOrderTotal(orderPageDTO);
    }

    //查看订单详情
    @Override
    public Map<String, Object> getOrderDetails(String orderNum, Integer userId, Integer usersource) {

        OTCOrder order = getOrderByOrderNum(orderNum);
        if (order == null) {
            LogUtils.warn(this.getClass(), "getOrderDetailsNoNumber orderNum: " + orderNum + "userId: " + userId);
            throw new IdaxException(OrderResponseEnum.ORDER_ISNOTEXIST.getRespCode());
        }

        Map<String, Object> map = new HashMap<>();

        //广告id
        Integer adid = order.getAdid();

        //用户信息
        OrderDetailsQuery orderDetailsQuery = new OrderDetailsQuery();
        orderDetailsQuery.setOrderNum(orderNum);
        if (userId.intValue() == order.getBuyer().intValue()) {
            //当前是买家 显示卖家信息
            Integer seller = order.getSeller();
            UserCertificateName userCertificateName = userService.getUserCertificateName(order.getBuyer(), seller);
            orderDetailsQuery.setUserId(seller);
            OrderDetailsResultDTO orderDetailsResult = otcOrderMapper.getOrderDetails(orderDetailsQuery);
            //为移动端+H5准备,PC自己作了判断
            if (usersource.intValue() != 1) {
                orderDetailsResult.setType(OrderEnum.TYPE_BUY.getCode());
            }
            OrderDetailsDTO orderDetailsDTO = dealOrderDetails(orderDetailsResult, order.getBuyer(), order);
            orderDetailsDTO.setBuyerCertificateNameName(userCertificateName.getBuyerName());
            User user = userService.getUserById(order.getBuyer());
            orderDetailsDTO.setBuyerNickName(user.getNickname());
            //获取user信息
            UserInfoResultDTO userInfoResult = otcUserService.getUserInfo(seller);
            UserInfoDTO userInfo = makeUserInfoResult(userInfoResult, seller);
            map.put("orderInfo", orderDetailsDTO);
            map.put("userInfo", userInfo);
            map.put("myComment", otcCommentService.getOrderDetailCommentInfo(order.getBuyer(), orderNum));
            map.put("otherComment", otcCommentService.getOrderDetailCommentInfo(seller, orderNum));
            map.put("userCertificateName", userCertificateName);


            //是否已经加入信任名单
            if (otcUserGroupService.isContainedList(userId, seller, UserEnum.trust_List.getCode())) {
                map.put("inTrustList", false);
            } else {
                map.put("inTrustList", true);
            }
            //是否已经加入黑名单名单
            if (otcUserGroupService.isContainedList(userId, seller, UserEnum.black_List.getCode())) {
                map.put("inBlackList", false);
            } else {
                map.put("inBlackList", true);
            }

        } else if (userId.intValue() == order.getSeller().intValue()) {
            //当前是卖家 显示买家信息
            Integer buyer = order.getBuyer();
            UserCertificateName userCertificateName = userService.getUserCertificateName(buyer, order.getSeller());
            orderDetailsQuery.setUserId(buyer);
            OrderDetailsResultDTO orderDetailsResult = otcOrderMapper.getOrderDetails(orderDetailsQuery);
            //为移动端准备
            if (usersource.intValue() != 1) {
                orderDetailsResult.setType(OrderEnum.TYPE_SELL.getCode());
            }
            OrderDetailsDTO orderDetailsDTO = dealOrderDetails(orderDetailsResult, order.getSeller(), order);
            orderDetailsDTO.setBuyerCertificateNameName(userCertificateName.getBuyerName());
            orderDetailsDTO.setBuyerNickName(orderDetailsResult.getNickName());
            orderDetailsDTO.setQty(new BigDecimal(orderDetailsDTO.getQty()).setScale(6, BigDecimal.ROUND_DOWN).toPlainString());
            //获取user信息
            UserInfoResultDTO userInfoResult = otcUserService.getUserInfo(buyer);
            UserInfoDTO userInfo = makeUserInfoResult(userInfoResult, buyer);
            map.put("orderInfo", orderDetailsDTO);
            map.put("userInfo", userInfo);
            map.put("myComment", otcCommentService.getOrderDetailCommentInfo(order.getSeller(), orderNum));
            map.put("otherComment", otcCommentService.getOrderDetailCommentInfo(buyer, orderNum));
            map.put("userCertificateName", userCertificateName);

            //是否已经加入信任名单
            if (otcUserGroupService.isContainedList(userId, buyer, UserEnum.trust_List.getCode())) {
                map.put("inTrustList", false);
            } else {
                map.put("inTrustList", true);
            }
            //是否已经加入黑名单名单
            if (otcUserGroupService.isContainedList(userId, buyer, UserEnum.black_List.getCode())) {
                map.put("inBlackList", false);
            } else {
                map.put("inBlackList", true);
            }

        } else {
            //否则没权限查看
            throw new IdaxException(OrderResponseEnum.ORDER_WITHNOAUTH.getRespCode());
        }
//        OTCAds otcAds = otcAdsService.selectByPrimaryKey(adid);
        if (userId.equals(order.getAdvertiser())) {
            map.put("isPublisher", 1);//当前用户是广告主
        } else {
            map.put("isPublisher", 2);//当前用户不是广告主
        }

        //收款账号信息 买单  显示卖家信息 卖单  显示卖家信息
        List<OTCBeneficiarys> otcBeneficiarys = otcBeneficiarysService.showDisplayBeneficiarys(order.getSeller());

        //老的的支付信息查询方法
//        List<PayMethodDetailDTO> payMethodDetailList = otcBeneficiarysService.getPayMethodDetail(adid);

        List<PayMethodDetailDTO> list = makePayMethod(otcBeneficiarys);
        //该笔订单当前用户是否已经评论过
        if (otcCommentService.getCommentByNumAndCommentUserId(orderNum, userId) != null) {
            map.put("commented", true);
        } else {
            map.put("commented", false);
        }
        map.put("payMethod", list);
        return map;
    }

    //封装收款信息
    private List<PayMethodDetailDTO> makePayMethod(List<OTCBeneficiarys> payMethodDetailList) {
        List<PayMethodDetailDTO> list = new ArrayList<>();
        for (OTCBeneficiarys otcBeneficiarys : payMethodDetailList) {
            PayMethodDetailDTO payMethodDetail = new PayMethodDetailDTO();
            payMethodDetail.setTradeType(otcBeneficiarys.getDictitemid());
            payMethodDetail.setAccount(otcBeneficiarys.getAccount());
            payMethodDetail.setName(otcBeneficiarys.getName());
            payMethodDetail.setBankname(otcBeneficiarys.getBankname());
            payMethodDetail.setSubbranchbankname(otcBeneficiarys.getSubbranchbankname());
            payMethodDetail.setAttachmenturl(fileUrlService.dealFileUrlAddress(otcBeneficiarys.getAttachmenturl()));

            PayTypeInfo payTypeInfo = new PayTypeInfo();
            payTypeInfo.setPayTypeNation(OTCPayTypeNationUtils.getPayType(otcBeneficiarys.getDictitemid()));
            payTypeInfo.setPayTypeIcon(OTCPayTypeNationUtils.getPayTypeIcon(otcBeneficiarys.getDictitemid()));

            payMethodDetail.setPayTypeInfo(payTypeInfo);
            list.add(payMethodDetail);
        }
        return list;
    }

    private UserInfoDTO makeUserInfoResult(UserInfoResultDTO userInfoResult, Integer userId) {
        Integer trustNumber = otcUserGroupMapper.countTrustGroupTotalByUserId(userId);
        return new UserInfoDTO(userInfoResult.getTradenumber(), trustNumber, userInfoResult.getHistoryTurnover(), userInfoResult.getGoodrate(),
                userInfoResult.getGoodNumber(), userInfoResult.getBadNumber(), userInfoResult.getIsemailverify(), userInfoResult.getIscertificate(), userInfoResult.getMobile());
    }

    /**
     * 调用之前必须判断参数不为空
     *
     * @param orderChangeDTO
     * @return
     */
    int updateOrderState(OrderChangeDTO orderChangeDTO) {

        //更新之前先查
        OTCOrder needUpdateOrder = getOrderByOrderNum(orderChangeDTO.getOrderNum());

        //如果两个状态一样
        if (orderChangeDTO.getOrderState().equals(needUpdateOrder.getOrderstate())) {
            throw new IdaxException(OrderResponseEnum.ORDER_STATE_IS_CURRENT.getRespCode());
        }

        //修改订单状态和更新时间 + rowversion
        int updateFlag = otcOrderMapper.updateOrderState(new UpdateOrderStateDTO(needUpdateOrder.getId(), orderChangeDTO.getOrderState(),
                DateUtils.getUTCNow(), needUpdateOrder.getRowversion()));

        return updateFlag;
    }

    //下单
    @Override
    public OTCOrder createOrder(CreateOrderDTO createOrderDTO, OTCAds otcAds) {

        Integer userId = createOrderDTO.getUserId();

        //广告发布者id
        Integer publisherId = otcAds.getPublisherid();
        //购买的数量
        BigDecimal qty = createOrderDTO.getQty();
        //广告交易类型(1-出售 2-购买)
        Integer type = otcAds.getType();

        User user = userService.getUserById(userId);

        //不能买自己的广告
        if (userId.intValue() == publisherId.intValue()) {
            throw new IdaxException(OrderResponseEnum.ORDER_BUYBYSELF.getRespCode());
        }

        //账户是虚拟账户不允许下单
        if (user.getIsfake()) {
            throw new IdaxException(OrderResponseEnum.CREARE_ORDER_NO_AUTH.getRespCode());
        }

        //otcuser是否被冻结
        OTCUser otcUser = otcUserService.getOTCUserByUserId(userId);
        if (otcUser.getUserstatus().intValue() == OTCUserEnum.forzen.getCode().intValue()) {
            throw new IdaxException(OrderResponseEnum.BUY_ACCOUNTISFROZEN.getRespCode());
        }

        // 获取用户取消次数
        Long cancelOrderNum = this.getCancelOrderNum(userId);
        if (CommonConstant.MAX_CANCEL_ORDER_NUM <= cancelOrderNum) {
            throw new IdaxException(OrderResponseEnum.OTCCANCELORDER_MAX.getRespCode());
        }

        //广告实名时用户也需实名 与app 2.3冲突了 移除
//        if (otcAds.getIscertificate() && !user.getIscertificate()) {
//            throw new IdaxException(OrderResponseEnum.BUY_MUSTCERTIFICATE.getRespCode());
//        }

        //app 2.3 场外交易必须通过初级认证
        if (!user.getIscertificate()) {
            throw new IdaxException(OrderResponseEnum.OTC_NEED_CERTIFICATE.getRespCode());
        }

        //需要手机认证 app 2.3
        if (otcAds.getIsmoible() && StringUtils.isEmpty(user.getMobile())) {
            throw new IdaxException(OrderResponseEnum.BUY_MUST_MOBILE.getRespCode());
        }

        //需要高级认证 app 2.3
        if (otcAds.getIsadvancedcertificate() && !user.getIsadvancedcertificate()) {
            throw new IdaxException(OrderResponseEnum.BUY_MUST_ADVANCECERTIFICATE.getRespCode());
        }

        List<UserPageListDTO> publishAdBlackList = otcUserMapper.getUserBlackList(otcAds.getPublisherid());
        //发广告人的黑名单list中的任何一个userId匹配
        if (publishAdBlackList.stream().anyMatch(s -> s.getUniqueKey().equals(otcUserService.getOTCUserByUserId(userId).getUniquekey()))) {
            throw new IdaxException(OrderResponseEnum.BUY_INBLACKLIST.getRespCode());
        }

        List<UserPageListDTO> placeOrderBlackList = otcUserMapper.getUserBlackList(userId);

        //下单人的黑名单list中的任何一个userId匹配
        if (placeOrderBlackList.stream().anyMatch(s -> s.getUniqueKey().equals(otcUserService.getOTCUserByUserId(otcAds.getPublisherid()).getUniquekey()))) {
            throw new IdaxException(OrderResponseEnum.BUY_INBLACKLIST.getRespCode());
        }
        // 2.校验业务数据
        OrderProcessLogFunction.logOrderProcess(s -> checkBusinsessParam(createOrderDTO, otcAds), this.getClass(), LogOrderProcessEnum.order_create_check_param.getMessage());


        List<OTCSysDictionaryItem> otcSysDictionaryItems = otcSysDictionaryItemService.listItem(DictEnum.COIN_FEE_RATE.getCode());
        if (otcSysDictionaryItems.isEmpty()) {
            throw new IdaxException(OrderResponseEnum.COIN_FEE_RATE_ISEMPTY.getRespCode());
        }
        //手续费费率
        BigDecimal coinFeeRate = new BigDecimal(otcSysDictionaryItems.get(0).getItemdesc());
//        BigDecimal coinFeeRate = CommonConstant.COINFEERATE;

        OTCOrder placeOrder = new OTCOrder();
        if (type.intValue() == AdEnum.type_sell.getCode().intValue()) {//广告是卖单 买家是当前用户 卖家是广告发布者 扣除卖家的手续费
            //买卖双方未有未支付的订单
            if (getNumbereOfUnpaid(userId, publisherId) != 0) {
                throw new IdaxException(OrderResponseEnum.HAVA_UNPAID_ORDER.getRespCode());
            }
            placeOrder.setBuyer(userId);
            placeOrder.setSeller(publisherId);
            placeOrder.setSellerprocedurefee(qty.multiply(coinFeeRate).setScale(8, BigDecimal.ROUND_DOWN));
            placeOrder.setSellerprocedurefeerate(coinFeeRate);
        } else if (type.intValue() == AdEnum.type_buy.getCode().intValue()) {//广告是买单 买家是广告发布者 卖家是当前用户 扣除买家的手续费
            //买卖双方未有未支付的订单
            if (getNumbereOfUnpaid(publisherId, userId) != 0) {
                throw new IdaxException(OrderResponseEnum.HAVA_UNPAID_ORDER.getRespCode());
            }
            placeOrder.setBuyer(publisherId);
            placeOrder.setSeller(userId);
            placeOrder.setBuyerprocedurefee(qty.multiply(coinFeeRate).setScale(8, BigDecimal.ROUND_DOWN));
            placeOrder.setBuyerprocedurefeerate(coinFeeRate);
        }

        //执行下单 + 更新用户资金
        OTCOrder order = new OTCOrder();

        OTCOrder baseOrder = otcOrderService.placeOrder(createOrderDTO, otcAds, placeOrder, coinFeeRate);
        BeanUtils.copyProperties(baseOrder, order);


        return order;
    }

    @Override
    public CreateOrderResponse sendCreateOrderMessage(OTCOrder order) {

        CreateOrderResponse createOrderResponse = CreateOrderResponse.makeCreateOrder(BaseResponse.SUCCESS_CODE, BaseResponse.SUCCESS_MSG, new OrderNumDTO(order.getNumber()));

        OrderProcessLogFunction.logOrderProcess(s -> {
            //发消息 状态 1
            pushOrderStateMessage(order, OrderState.STATE_UNPAID.getCode(), order.getBuyer());
            // 发送我的通知消息

            LogUtils.info(this.getClass(), "mongo地址是***" + monggoURI);
            MessageNotifyUtils.sendNotify(order.getAdvertiser(),
                    order.getNumber(),
                    MessageNotifyEnum.BodyTemplate.order_trade,
                    MessageNotifyUtils.getNotifyNickName(order.getOwner()));
            //发送邮件通知 这里只发给广告主 with cuiyongshu 2018-12-13
            String notifyNickName = MessageNotifyUtils.getNotifyNickName(order.getOwner());
            DownOrderDTO adPublish = new DownOrderDTO();
            adPublish.setQty(order.getQty());
            adPublish.setCoinCode(order.getPairname());
            adPublish.setNickName(notifyNickName);
            adPublish.setOrderId(order.getNumber());
            User adPublisher = userService.getUserById(order.getAdvertiser());
            //广告主语言
            String language = userLanguageService.getUsererSimpleLanguage(adPublisher.getId()).getLanguage();
            if (order.getType().intValue() == CommonConstant.OrderType.seller) {
                //发送给广告主 卖单 购买   用户“张三”准备向你购买xxxBTC，订单号为001120181212070409000061
                sqsService.sendEmailMessage(CommonConstant.EmailType.DOWN_ORDER_SELL, adPublisher.getEmail(),
                        language, order.getId(), sqsQueueName, adPublish);
            } else {
                //发送给广告主    用户“张三”准备向你出售xxxBTC，订单号为001120181212070409000061s
                sqsService.sendEmailMessage(CommonConstant.EmailType.DOWN_ORDER_BUY, adPublisher.getEmail(),
                        language, order.getId(), sqsQueueName, adPublish);
            }
            //极光推送给广告主  用户{0}给您发来了新的交易请求
            jiGuangService.pushTemplate(order, adPublisher.getEmail(), order.getAdvertiser(),
                    order.getBuyer().equals(order.getOwner()) ? false : true, language, JiGuangPushEnum.ORDER_CREATE, notifyNickName);


        }, this.getClass(), LogOrderProcessEnum.order_create_place_add_push_message.getMessage());
        return createOrderResponse;
    }

    //执行下单
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {RuntimeException.class, Exception.class})
    public OTCOrder placeOrder(CreateOrderDTO createOrderDTO, OTCAds otcAds, OTCOrder order, BigDecimal coinFeeRate) {

        //广告交易类型(1-出售 2-购买)
        Integer type = otcAds.getType();
        //购买的数量
        BigDecimal qty = createOrderDTO.getQty();

        // 冻结数量（交易金额 + 手续费）
        BigDecimal frozenQty = qty;
        Integer userId = order.getSeller();

        Account account = accountService.getAccountByCoinIdAndUserId(userId, otcAds.getPairid());
        if (account == null || account.getAvailableamount().compareTo(createOrderDTO.getQty()) < 0) {
            LogUtils.info(this.getClass(), "placeOrderMethod account: " + JSON.toJSONString(account) + "orderQty: " + qty.toPlainString() + "sellerId: " + userId);
            throw new IdaxException(OrderResponseEnum.SELLER_ACCOUNT_NOT_ENOUGH.getRespCode());
        }

        // 获取当日下单数量
        BigDecimal currentPlaceOrderQty = new BigDecimal(this.getPlaceOrderQty());
        Integer orderId = getOrderId();
        order.setId(orderId);
        order.setUniquekey(UUidUtils.getUUID());
        order.setNumber(OrderUtils.createOrderNumber(currentPlaceOrderQty.toPlainString(), type, otcAds.getPairname()));
        order.setType(AdEnum.type_buy.getCode().equals(type) ? OrderEnum.TYPE_BUY.getCode() : OrderEnum.TYPE_SELL.getCode());//1 买 2 卖
        order.setAdid(otcAds.getId());
        order.setPairname(otcAds.getPairname());
        order.setCoinid(otcAds.getPairid());
        order.setPairprice(createOrderDTO.getPrice());//单价
        order.setQty(qty);
        order.setMoneytype(1);//目前只有cny
        order.setMoney(createOrderDTO.getTotalPrice());
        order.setOrderstate(OrderState.STATE_UNPAID.getCode());
        order.setOrdersubstate(OrderState.SUB_STATE_INITIALIZATION.getSubCode());
        order.setOrdersource(createOrderDTO.getOrderSource());
        order.setCreatetime(DateUtils.getUTCNow());
        order.setUpdatetime(order.getCreatetime());
        order.setExpiretime(expireTime(order.getCreatetime()));
        order.setOwner(createOrderDTO.getUserId());
        order.setUnit(otcAds.getUnit());

        order.setAdvertiser(otcAds.getPublisherid());
        int createOrder = otcOrderMapper.insertSelective(order);

        //下单失败
        if (createOrder <= 0) {
            throw new IdaxException(OrderResponseEnum.ORDER_CREATE_FAIL.getRespCode());
        }
        order.setId(orderId);
        //更新orderlog 状态
        if (otcOrderLogService.insertOtcOrderLog(order, OrderState.STATE_UNPAID.getSubCode()) <= 0) {
            throw new IdaxException(OrderResponseEnum.ORDERLOG_INSERTFAIL.getRespCode());
        }

        // 调用接口进行 下单之前冻结用户账户部分资金并作判断 可用-冻结 >=0 + 手续费
        ChangeListData changeListData = ChangeListData.builder()
                .accountId(account.getId())
                .userId(order.getSeller())
                .coinId(otcAds.getPairid())
                .coinCode(otcAds.getPairname())
                .transactionType(AccountTransactionEnum.TRANSACTIONTYPE_CREATEORDER.getCode())
                .sourceId(orderId)
                .remark(AccountTransactionEnum.TRANSACTIONTYPE_CREATEORDER.getRemark())
                .availableChange(frozenQty.multiply(new BigDecimal("-1")))
                .frozenChange(frozenQty)
                .build();

        LogUtils.info(this.getClass(), "otcCreateOrderParams, orderNum: " + order.getNumber() + "qty: " + order.getQty() + " availableChange " + changeListData.getAvailableChange());

        if (!accountCapitalService.changeAccount(changeListData, CapitalAPIEnum.CREATE_ORDER)) {
            LogUtils.info(this.getClass(), "otc_create_order fail, adInfo " + JSON.toJSONString(otcAds));
            throw new IdaxException(OrderResponseEnum.ORDERE_FROZENACCOUNTFAIL.getRespCode());
        }

        return order;
    }

    //获取订单状态为1 下单时间大于15分钟的订单
    @Override
    public List<OTCOrder> getOrderPayTimeout(Date date) {
        List<OTCOrder> otcOrders = otcOrderMapper.getOrderPayTimeout(date);
        return otcOrders;
    }

    //校验业务数据
    private BaseResponse checkBusinsessParam(CreateOrderDTO createOrderDTO, OTCAds otcAds) {
        //购买总金额区间
        BigDecimal totalPrice = createOrderDTO.getTotalPrice();

        int compareMin = totalPrice.compareTo(otcAds.getMinamount());
        int compareMax = totalPrice.compareTo(otcAds.getMaxamount());
        // 判断如果为购买广告需比较数量
        if (AdEnum.type_buy.getCode().equals(otcAds.getType())) {
            compareMin = createOrderDTO.getQty().compareTo(otcAds.getMinamount());
            compareMax = createOrderDTO.getQty().compareTo(otcAds.getMaxamount());

            List<OTCBeneficiarys> listBeneficiarys = this.otcBeneficiarysService.selectByUserID(createOrderDTO.getUserId());
            if (listBeneficiarys.isEmpty()) {
                throw new IdaxException(OrderResponseEnum.BENEFICIARY_SELF_NONE.getRespCode());
            }
            //广告为买单 出售人为当前下单人，必须开启支付方式
            List<OTCBeneficiarys> otcBeneficiarys = otcBeneficiarysService.showDisplayBeneficiarys(createOrderDTO.getUserId());
            if (otcBeneficiarys.isEmpty()) {
                throw new IdaxException(OrderResponseEnum.OTCBENEFICIARY_ORDERINGPERSON_EMPTY.getRespCode());
            }
        } else {
            //广告为卖单 出售人为广告主，必须开启支付方式
            List<OTCBeneficiarys> otcBeneficiarys = otcBeneficiarysService.showDisplayBeneficiarys(otcAds.getPublisherid());
            if (otcBeneficiarys.isEmpty()) {
                throw new IdaxException(OrderResponseEnum.OTCBENEFICIARY_ADER_EMPTY.getRespCode());
            }
        }
        //购买总额小于最小额
        if (compareMin < 0) {
            LogUtils.info(this.getClass(), "checkBusinsessParamCompareMin totalPrice: " + totalPrice + " ,minamount(): " + otcAds.getMinamount() + " ,qty: " + createOrderDTO.getQty());
            throw new IdaxException(OrderResponseEnum.ORDER_TOTALPRICE_MINERROR.getRespCode());
            //购买总额大于最大额
        } else if (compareMax > 0) {
            LogUtils.info(this.getClass(), "checkBusinsessParamCompareMax totalPrice: " + totalPrice + " ,maxamount(): " + otcAds.getMaxamount() + " ,qty: " + createOrderDTO.getQty());
            throw new IdaxException(OrderResponseEnum.ORDER_TOTALPRICE_MAXERROR.getRespCode());
        }

        //广告状态必须为上架
        if (!otcAds.getState().equals(AdEnum.state_up.getCode())) {
            throw new IdaxException(OrderResponseEnum.AD_STATE_NOTSUPPORT.getRespCode());
        }
        return null;
    }

    //基础参数校验
    private BaseResponse checkParam(CreateOrderDTO createOrderDTO) {

        Integer adId = createOrderDTO.getAdId();
        if (adId == null) {
            throw new IdaxException(OrderResponseEnum.ORDER_ADIDISEMPTY.getRespCode());
        }
        BigDecimal totalPrice = createOrderDTO.getTotalPrice();
        if (totalPrice.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IdaxException(OrderResponseEnum.ORDER_TOTALPRICEISERROR.getRespCode());
        }
        BigDecimal qty = createOrderDTO.getQty();
        if (qty.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IdaxException(OrderResponseEnum.ORDER_QTYISERROR.getRespCode());
        }
        return null;
    }


    //封装订单详情数据
    private OrderDetailsDTO dealOrderDetails(OrderDetailsResultDTO orderDetailsResult, Integer userId, OTCOrder order) {
        OrderDetailsDTO orderDetails = new OrderDetailsDTO();
        if (orderDetailsResult == null) {
            return orderDetails;
        }

        User user = userService.getUserById(userId.intValue() == order.getBuyer().intValue() ? order.getSeller() : order.getBuyer());
        orderDetails.setDescription(orderDetailsResult.getDescription());
        orderDetails.setRemark(orderDetailsResult.getRemark());
        orderDetails.setNickName(StringUtils.isEmpty(orderDetailsResult.getNickName()) ? orderDetailsResult.getEmail() : orderDetailsResult.getNickName());
        orderDetails.setType(orderDetailsResult.getType());//1 买 2 卖
        orderDetails.setNumber(orderDetailsResult.getNumber());
        orderDetails.setTotalMoney(((orderDetailsResult.getTotalMoney().setScale(2, BigDecimal.ROUND_DOWN)).toPlainString()));
        orderDetails.setQty(orderDetailsResult.getQty().toPlainString());
        orderDetails.setPairPrice(((orderDetailsResult.getPairPrice().setScale(2, BigDecimal.ROUND_DOWN)).toPlainString()));
        orderDetails.setOrderKey(orderDetailsResult.getOrderKey());
        orderDetails.setUserKey(orderDetailsResult.getUserKey());
        orderDetails.setPair(orderDetailsResult.getPair());
        orderDetails.setExpireTime(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(orderDetailsResult.getExpireTime()));
        orderDetails.setCreateTime(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(orderDetailsResult.getCreateTime()));
        orderDetails.setCountdown(DateUtils.subUTCDate(orderDetailsResult.getExpireTime(), DateUtils.getUTCNow(), "00:mm:ss"));
        orderDetails.setOrderState(orderDetailsResult.getOrderState());
        orderDetails.setIconUrl(fileUrlService.dealFileUrlAddress(orderDetailsResult.getIconUrl()));
        orderDetails.setTradeType(orderDetailsResult.getTradeType());
        orderDetails.setMobile(user.getMobile());
        if (otcArbitrationService.getOTCArbitration(userId, orderDetailsResult.getId()) == null) {
            orderDetails.setArbitration(false);
        } else {
            orderDetails.setArbitration(true);
        }
        orderDetails.setUnit(orderDetailsResult.getUnit());
        orderDetails.setPayTypeIcon(OTCPayTypeNationUtils.getPayTypeIcon(orderDetailsResult.getTradeType()));
        Integer sellType = orderDetailsResult.getSellType();
        orderDetails.setSellType(sellType);
        if (!Objects.isNull(sellType)) {
            orderDetails.setBuyerPayTypeIcon(OTCPayTypeNationUtils.getPayTypeIcon(sellType));
        }
        //获取该笔订单买家勾选的卖家收款信息
        if (!Objects.isNull(order.getSelltype())) {
            OTCBeneficiarys otcBeneficiarys = otcBeneficiarysService.getOTCBeneficiarysByuserIdAndDictItemId(order.getSeller(), order.getSelltype());
            orderDetails.setReceiptAccount(otcBeneficiarys.getAccount());
            orderDetails.setReceiptName(otcBeneficiarys.getName());
            orderDetails.setReceiptBankname(otcBeneficiarys.getBankname());
            orderDetails.setReceiptSubbranchbankname(otcBeneficiarys.getSubbranchbankname());
            orderDetails.setReceiptAttachmenturl(otcBeneficiarys.getAttachmenturl());
        }


        return orderDetails;
    }


    /**
     * 更新订单状态并发送通知
     * 调接口之前 需要先判断订单是否存在+用户是否可以修改订单--暂时还没改
     *
     * @param orderChangeDTO
     * @param userId
     * @return
     */
    @Override
    public Boolean updateOrderStateCommon(OTCOrder order, OrderChangeDTO orderChangeDTO, Integer userId) {

        //只有买家和卖家才可以更新订单状态
        if (userId.intValue() != order.getBuyer().intValue() && userId.intValue() != order.getSeller().intValue()) {
            throw new IdaxException(OrderResponseEnum.UPDATE_WITHNOAUTH.getRespCode());
        }

        int updateFlag = updateOrderState(orderChangeDTO);
        if (updateFlag != 1) {
            throw new IdaxException(OrderResponseEnum.UPDATE_ORDERSTATEFAIL.getRespCode());
        }

        return true;
    }

    @Override
    public Boolean updateOrderStateCommon(OTCOrder order, OTCOrder oldOrder, Integer userId) {

        //只有买家和卖家才可以更新订单状态
        if (userId.intValue() != oldOrder.getBuyer().intValue() && userId.intValue() != oldOrder.getSeller().intValue()) {
            throw new IdaxException(OrderResponseEnum.UPDATE_WITHNOAUTH.getRespCode());
        }

        //如果两个状态一样
        if (oldOrder.getOrderstate().equals(order.getOrderstate())) {
            throw new IdaxException(OrderResponseEnum.ORDER_STATE_IS_CURRENT.getRespCode());
        }

        order.setUpdatetime(DateUtils.getUTCNow());
        order.setId(null);
        order.setRowversion(null);

        OTCOrderExample example = new OTCOrderExample();
        example.createCriteria()
                .andRowVersionEqualTo(oldOrder.getRowversion())
                .andOrderstateEqualTo(oldOrder.getOrderstate())
                .andIdEqualTo(oldOrder.getId());
        //修改订单状态和更新时间 + rowversion
        int updateFlag = otcOrderMapper.updateByExampleSelective(order, example);
        if (updateFlag != 1) {
            LogUtils.warn(this.getClass(), "updateOrderStateCommon fail,orderNum: " + oldOrder.getNumber());
            throw new IdaxException(OrderResponseEnum.UPDATE_ORDERSTATEFAIL.getRespCode());
        }

        return true;
    }

    /**
     * 订单状态改变后push消息
     *
     * @param order
     * @param orderState
     * @param userId     触发者userid
     */
    @Override
    public void pushOrderStateMessage(OTCOrder order, Integer orderState, Integer userId) {

        User buyer = userService.getUserById(order.getBuyer());
        User seller = userService.getUserById(order.getSeller());
        String buyerNickname = NickNameUtils.concatNickName(buyer.getNickname(), buyer.getEmail());
        String sellerNickname = NickNameUtils.concatNickName(seller.getNickname(), seller.getEmail());

        // 下单人
        String ownerName = ofNullable(this.userService.getUserById(order.getOwner())).map(u -> NickNameUtils.concatNickName(u.getNickname(), u.getEmail())).orElse("");
        // 广告主
        String advertiserName = ofNullable(this.userService.getUserById(order.getAdvertiser())).map(u -> NickNameUtils.concatNickName(u.getNickname(), u.getEmail())).orElse("");

        String channelTopic = CommonConstant.SENDORDERSTATANDORDERNUM;
        try {

            OrderChat orderChat = new OrderChat();
            orderChat.setOrderState(orderState);
            orderChat.setMsgType(MessageTypeEnum.text);
            orderChat.setLogType(LogTypeEnum.system);
            orderChat.setOrderId(order.getUniquekey());
            orderChat.setSenderId(CommonConstant.SENDERID);
            orderChat.setMessageId(UUidUtils.getUUID());
            orderChat.setBuyerId(order.getBuyer());
            orderChat.setSellerId(order.getSeller());
            orderChat.setOrderNum(order.getNumber());
            Object[] params = new Object[0];
            //推送消息
            if (orderState.equals(OrderState.STATE_UNPAID.getCode())) {
                //未支付
                OTCAds otcAds = otcAdsService.selectByPrimaryKey(order.getAdid());
                if (StringUtils.isNotEmpty(otcAds.getWelcomenote())) {
                    orderChat.setSenderId(order.getAdvertiser());
                    orderChat.setLogType(order.getBuyer().equals(order.getAdvertiser()) ? LogTypeEnum.buyer : LogTypeEnum.seller);
                    orderChat.setMessage(otcAds.getWelcomenote());
                    orderChat.setMessageId(UUidUtils.getUUID());
                    orderChat.setUserLogo(otcUserService.getUserIconUrl(otcAds.getPublisherid()));
                    orderChat.setBuyerReadFlag(otcAds.getType().equals(AdEnum.type_buy.getCode()) ? 1 : 0);
                    orderChat.setSellerReadFlag(otcAds.getType().equals(AdEnum.type_sell.getCode()) ? 1 : 0);
                    if (this.saveChatLogToMongo(orderChat) == null) {
                        orderChat.setSaved(false);
                        LogUtils.error(this.getClass(), "saveAdInfoToMongo fail, ", order.getAdid());
                    }
                    redisTemplate.convertAndSend(channelTopic, JSON.toJSONString(orderChat));
                }
                orderChat.setSenderId(CommonConstant.SENDERID);
                orderChat.setLogType(LogTypeEnum.system);
                orderChat.setMessageId(UUidUtils.getUUID());
                orderChat.setMessage(MessageNotifyEnum.ChatTemplate.order_trade.getBodyCode());
                params = new Object[]{ownerName, advertiserName};


            } else if (orderState.equals(OrderState.STATE_TORELEASED.getCode())) {
                //卖家标识为放行
                orderChat.setMessage(MessageNotifyEnum.ChatTemplate.order_release.getBodyCode());
                params = new Object[]{sellerNickname};
            } else if (orderState.equals(OrderState.STATE_PAID.getCode())) {
                //买家已支付
                orderChat.setMessage(MessageNotifyEnum.ChatTemplate.order_pay.getBodyCode());
                params = new Object[]{buyerNickname};
            } else if (orderState.equals(OrderState.STATE_FINISH.getCode())) {
                //已完成
                OTCAds otcAds = otcAdsService.selectByPrimaryKey(order.getAdid());
                if (StringUtils.isNotEmpty(otcAds.getEndnote())) {
                    orderChat.setSenderId(order.getAdvertiser());
                    orderChat.setLogType(order.getBuyer().equals(order.getAdvertiser()) ? LogTypeEnum.buyer : LogTypeEnum.seller);
                    orderChat.setMessage(otcAds.getEndnote());
                    orderChat.setMessageId(UUidUtils.getUUID());
                    orderChat.setUserLogo(otcUserService.getUserIconUrl(otcAds.getPublisherid()));
                    orderChat.setBuyerReadFlag(otcAds.getType().equals(AdEnum.type_buy.getCode()) ? 1 : 0);
                    orderChat.setSellerReadFlag(otcAds.getType().equals(AdEnum.type_sell.getCode()) ? 1 : 0);
                    if (this.saveChatLogToMongo(orderChat) == null) {
                        orderChat.setSaved(false);
                        LogUtils.error(this.getClass(), "saveAdInfoToMongo fail,adId ", order.getAdid());
                    }
                    redisTemplate.convertAndSend(channelTopic, JSON.toJSONString(orderChat));
                }
                orderChat.setSenderId(CommonConstant.SENDERID);
                orderChat.setLogType(LogTypeEnum.system);
                orderChat.setMessageId(UUidUtils.getUUID());
                orderChat.setMessage(MessageNotifyEnum.ChatTemplate.order_finish.getBodyCode());
                params = new Object[]{sellerNickname, buyerNickname};
            } else if (orderState.equals(OrderState.STATE_BUYER_CANCEL.getCode())) {
                //买家取消
                orderChat.setMessage(MessageNotifyEnum.ChatTemplate.order_cancel.getBodyCode());
                params = new Object[]{buyerNickname};
            } else {
                //超时未支付
                orderChat.setMessage(MessageNotifyEnum.ChatTemplate.order_expire.getBodyCode());
            }
//            else {
//                //其他状态
//                orderChat.setMessage(MessageNotifyUtils.getChatNotifyContent(orderState));
//            }

            orderChat.setDynamicParam(params);
            orderChat.setUserLogo(otcUserService.getUserIconUrl(userId));
            //已读 未读标识
            orderChat.setBuyerReadFlag(userId.equals(order.getBuyer()) ? 1 : 0);
            orderChat.setSellerReadFlag(userId.equals(order.getBuyer()) ? 0 : 1);

            if (this.saveChatLogToMongo(orderChat) == null) {
                orderChat.setSaved(false);
                LogUtils.error(this.getClass(), "saveOrderInfoToMongo fail,orderNum ", order.getAdid());
            }
            redisTemplate.convertAndSend(channelTopic, JSON.toJSONString(orderChat));

        } catch (Exception e) {
            LogUtils.warn(this.getClass(), "finish update orderState but send message fail" + e);
        }
    }

    /**
     * 保存消息到mongo
     *
     * @param orderChat
     * @return
     */
    private OrderChat saveChatLogToMongo(OrderChat orderChat) {
        try {
            return chatLogService.insert(orderChat);
        } catch (Exception e) {
            LogUtils.error(this.getClass(), "----saveChatLogToMongo chatLog to mongo error----", e);
        }
        return null;
    }


    //封装orderlist
    public List<OrderPageListDTO> getOrderResultList(List<OrderResultDTO> otcOrders, Integer userId) {
        List<OrderPageListDTO> orderPageListDTOS = new ArrayList<>();
        for (OrderResultDTO otcOrder : otcOrders) {
            OrderPageListDTO orderPage = new OrderPageListDTO();
            orderPage.setOrderNum(otcOrder.getNumber());
            orderPage.setPairPrice(((otcOrder.getPrice().setScale(2, BigDecimal.ROUND_DOWN)).toPlainString()));//单价
            orderPage.setTotalPrice(((otcOrder.getTotalPrice().setScale(2, BigDecimal.ROUND_DOWN)).toPlainString()));//总价+收款类型
            orderPage.setNickName(NickNameUtils.concatNickName(otcOrder.getNickName(), otcOrder.getEmail()));//o********m
            orderPage.setPair(otcOrder.getPair());//
            orderPage.setOrderstate(otcOrder.getOrderstate());
            //判断当前用户是否是订单买家 买家显示买 卖家显示卖
            if (userId.intValue() == otcOrder.getBuyer().intValue()) {
                orderPage.setType(OrderEnum.TYPE_BUY.getCode());
            } else {
                orderPage.setType(OrderEnum.TYPE_SELL.getCode());
            }
            orderPage.setAdId(otcOrder.getAdId());
            orderPage.setQty(otcOrder.getQty().setScale(6, BigDecimal.ROUND_DOWN).toPlainString());
            orderPage.setCreatetime(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(otcOrder.getCreatetime()));
            orderPage.setIscertificate(otcOrder.getIscertificate());
            orderPage.setIconurl(fileUrlService.dealFileUrlAddress(otcOrder.getIconurl()));
            orderPage.setUniquekey(otcOrder.getUniquekey());
            orderPage.setUnit(otcOrder.getUnit());
            orderPageListDTOS.add(orderPage);
        }
        return orderPageListDTOS;
    }

//    public String concatNickName(String nickName,String email){
//        if (StringUtils.isEmpty(nickName)){
//            return email.substring(0, 3).concat("********").concat(email.substring(nickName.lastIndexOf("@")));
//        }
////        return nickName.substring(0,1).concat("********").concat(nickName.substring(nickName.length()-1,nickName.length()));
//        return nickName;
//    }

    @Override
    public OTCOrder getOrderByOrderNumAndUserId(String orderNum, Integer userId) {
        OTCOrderExample example = new OTCOrderExample();
        example.createCriteria()
                .andNumberEqualTo(orderNum)
                .andBuyerEqualTo(userId);

        List<OTCOrder> otcOrders = otcOrderMapper.selectByExample(example);

        return ofNullable(otcOrders)
                .map(list -> list.get(0))
                .orElse(null);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {RuntimeException.class, Exception.class})
    public CapitalAPIParamsDTO releaseConin(OTCOrder otcOrder, Integer userId) throws IdaxException {

        CapitalAPIParamsDTO capitalAPIParamsDTO = null;

        LogUtils.info(this.getClass(), "releaseCoinCapitalAPIParamsDTONew 0 " + JSON.toJSONString(otcOrder));

        capitalAPIParamsDTO = otcOrderService.checkReleaseOrderServiceParams(otcOrder, userId);

        LogUtils.info(this.getClass(), "releaseCoinCapitalAPIParamsDTONew 1 " + JSON.toJSONString(capitalAPIParamsDTO));

        //如果插入失敗 || 非第一次插入
        if (!capitalAPIParamsDTO.getFirstInsert()) {
            throw new IdaxException(OrderResponseEnum.ORDERE_RELEASE_INSERT_FAIL.getRespCode());
        }

        // 2. 修改订单状态
        OTCOrder updateOrder = new OTCOrder();
        updateOrder.setOrderstate(OrderState.STATE_FINISH.getCode());

        LogUtils.info(this.getClass(), "releaseCoinCapitalAPIParamsDTONew 2 " + capitalAPIParamsDTO.getGuid());

        //orderlog
        if (otcOrderLogService.insertOtcOrderLog(otcOrder, OrderState.STATE_FINISH.getCode()) <= 0) {
            throw new IdaxException(OrderResponseEnum.ORDERLOG_INSERTFAIL.getRespCode());
        }

        // 2.1 判断是否修改成功(防止高并发问题)
//            this.updateOrderStateCommon(updateOrder, otcOrder, otcOrder.getSeller());
        updateOrderAtOrderFinish(updateOrder, otcOrder, otcOrder.getSeller());

        LogUtils.info(this.getClass(), "releaseCoinCapitalAPIParamsDTONew 3 " + capitalAPIParamsDTO.getGuid());
        //资金划转
        capitalAPIBaseService.excludeCreateOrderRequestAPI(capitalAPIParamsDTO);
        return capitalAPIParamsDTO;

    }

    private void updateOrderAtOrderFinish(OTCOrder order, OTCOrder oldOrder, Integer userid) {

        //只有卖家才可以更新订单状态
        if (userid.intValue() != oldOrder.getSeller().intValue()) {
            throw new IdaxException(OrderResponseEnum.UPDATE_WITHNOAUTH.getRespCode());
        }

        //如果两个状态一样
        if (oldOrder.getOrderstate().equals(order.getOrderstate())) {
            throw new IdaxException(OrderResponseEnum.ORDER_STATE_IS_CURRENT.getRespCode());
        }

        //更新订单为成交
        order.setUpdatetime(DateUtils.getUTCNow());
        order.setId(null);
        order.setRowversion(null);
        order.setTradesuccess(true);

        OTCOrderExample example = new OTCOrderExample();
        example.createCriteria()
                .andRowVersionEqualTo(oldOrder.getRowversion())
                .andOrderstateEqualTo(oldOrder.getOrderstate())
                .andIdEqualTo(oldOrder.getId());
        //修改订单状态和更新时间 + rowversion
        int updateFlag = otcOrderMapper.updateByExampleSelective(order, example);
        if (updateFlag != 1) {
            LogUtils.warn(this.getClass(), "updateOrderAtOrderFinishFail,orderNum: " + oldOrder.getNumber());
            throw new IdaxException(OrderResponseEnum.UPDATE_ORDERSTATEFAIL.getRespCode());
        }

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {RuntimeException.class, Exception.class})
    public boolean releasePostProcess(OTCOrder otcOrder) throws IdaxException {

        try {
            // 3. 解冻交易币种(需判断广告为出售、购买，不同广告类型解冻目标不同)
            OTCAds advertising = this.otcAdsService.selectByPrimaryKey(otcOrder.getAdid());
            if (advertising == null) {
                throw new IdaxException(OTCAdResponseEnum.DATA_NOT_EXIST.getCode());
            }

            // 4. 修改用户的交易量和成交额（买卖双方
            Coin coin = coinService.selectByPrimaryKey(otcOrder.getCoinid());
            // 4.1 获取法币全场
//            BigDecimal qtyRate = this.getQtyRate(otcOrder, coin, otcOrder.getPairname());
//            BigDecimal buyQtyAsBTC = qtyRate.multiply(otcOrder.getQty()).setScale(8, BigDecimal.ROUND_HALF_DOWN);
            BigDecimal buyQtyAsBTC = getTradeCoinConvertBtcQty(otcOrder.getPairname(), otcOrder.getQty());
            otcUserService.updateTransactionInfoBySellerAndBuyer(otcOrder.getSeller(), otcOrder.getBuyer(), buyQtyAsBTC);

            // 5.修改广告的成交量
            Integer oldTradenumber = advertising.getTradenumber();
            otcAdsService.updateTradenumberToOTCAds(advertising, oldTradenumber);

            return true;
        } catch (Exception e) {
            LogUtils.error(this.getClass(), " releasePostProcess : " + JSON.toJSONString(otcOrder) + ", " + ExceptionUtils.getStackTrace(e));
            throw e;
        }
    }

    /**
     * 放币成功后推送通知
     *
     * @param oldOrder
     */
    @Override
    public void sendMessageAtRelease(OTCOrder oldOrder) {
        List<SimpleUserLanguage> languages = userLanguageService.getBuyerAndSellerSimpleLanguage(oldOrder.getBuyer(), oldOrder.getSeller());
        //发送邮件通知
        //给买家   用户“张三”已成功向你释放XXXUSDT, 单价为6.81CNY/USDT，交易已完成。订单号为001120181212070409000061s
        BigDecimal price = oldOrder.getPairprice().stripTrailingZeros();
        User buyerUser = userService.getUserById(oldOrder.getBuyer());
        ReleaseConinDTO receiveConinDTO = new ReleaseConinDTO();
        receiveConinDTO.setQty(oldOrder.getQty());
        receiveConinDTO.setCoinCode(oldOrder.getPairname());
        receiveConinDTO.setPairprice(price);
        receiveConinDTO.setOutCoinCode(oldOrder.getUnit());
        receiveConinDTO.setInCoinCode(oldOrder.getPairname());
        String notifyNickName = MessageNotifyUtils.getNotifyNickName(oldOrder.getSeller());
        receiveConinDTO.setNickName(notifyNickName);
        receiveConinDTO.setOrderId(oldOrder.getNumber());
        sqsService.sendEmailMessage(CommonConstant.EmailType.RELEASE_BUYER, buyerUser.getEmail(), languages.get(0).getLanguage(), oldOrder.getId(),
                sqsQueueName, receiveConinDTO);

        //给卖家   你已成功向“李四”释放XXXXUSDT，出售单价为6.81 CNY/USDT, 交易完成。订单号为001120181212070409000061s
        User sellerUser = userService.getUserById(oldOrder.getSeller());
        ReleaseConinDTO releaseConinDTO = new ReleaseConinDTO();
        releaseConinDTO.setQty(oldOrder.getQty());
        releaseConinDTO.setCoinCode(oldOrder.getPairname());
        releaseConinDTO.setPairprice(price);
        releaseConinDTO.setOutCoinCode(oldOrder.getUnit());
        releaseConinDTO.setInCoinCode(oldOrder.getPairname());
        releaseConinDTO.setNickName(MessageNotifyUtils.getNotifyNickName(oldOrder.getBuyer()));
        releaseConinDTO.setOrderId(oldOrder.getNumber());
        sqsService.sendEmailMessage(CommonConstant.EmailType.RELEASE_SELLER, sellerUser.getEmail(), languages.get(1).getLanguage(), oldOrder.getId(),
                sqsQueueName, releaseConinDTO);
        //极光推送给买家  卖家{0}已确认款项入账，并给您发放数字货币
        jiGuangService.pushTemplate(oldOrder, buyerUser.getEmail(), oldOrder.getBuyer(), true, languages.get(0).getLanguage(), JiGuangPushEnum.ORDER_FINISH, notifyNickName);
    }

    /**
     * 数字币的BTC价格
     *
     * @param otcOrder
     * @param coin
     * @param pair
     * @return
     */
    BigDecimal getQtyRate(OTCOrder otcOrder, Coin coin, String pair) {
        BigDecimal qtyRate = BigDecimal.ZERO;
        //btc
        if (ReferenceExchageConstant.marketPairs[0].equals(otcOrder.getPairname().toUpperCase())) {
            return BigDecimal.ONE;
        }
        //eth
        if (ReferenceExchageConstant.marketPairs[1].equals(otcOrder.getPairname())) {
            Object eth_btc = MemoryService.MARKETMAP.get("ETH_BTC");
            if (eth_btc != null) {
                Market market = JSON.parseObject(eth_btc.toString(), Market.class);
                if (market.getLastPrice() != null) {
                    qtyRate = market.getLastPrice();
                }
            }
        }
        //usdt
        if (ReferenceExchageConstant.marketPairs[2].equals(otcOrder.getPairname())) {
            //市场里面存的是BTC_USDT
            Object btc_usdt = MemoryService.MARKETMAP.get("BTC_USDT");
            if (btc_usdt != null) {
                Market market = JSON.parseObject(btc_usdt.toString(), Market.class);
                if (market.getLastPrice() != null) {
                    qtyRate = BigDecimal.ONE.divide(market.getLastPrice(), 8, RoundingMode.DOWN);
                }
            }
        }
        return qtyRate;
    }


    //标记付款已完成
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {RuntimeException.class, Exception.class})
    public Boolean confirmPaid(String orderNum, Integer payMethod, Integer userId) {
        //订单是否存在
//        OTCOrder order = getOTCOrderByUniqueKey(uniqueKey);
        OTCOrder oldOrder = getOrderByOrderNum(orderNum);
        if (oldOrder == null) {
            throw new IdaxException(OrderResponseEnum.ORDER_ISNOTEXIST.getRespCode());
        }
        OTCOrder order = new OTCOrder();
        //判断付款人是否是买家
        if (oldOrder.getBuyer().intValue() != userId.intValue()) {
            throw new IdaxException(OrderResponseEnum.ORDER_PAYAUTHBUYER.getRespCode());
        }

        //订单状态必须为 1 时才可以
        if (oldOrder.getOrderstate().intValue() != OrderState.STATE_UNPAID.getCode().intValue()) {
            throw new IdaxException(OrderResponseEnum.ORDERSTATE_ERROR.getRespCode());
        }

        //更新order sellType,sellAccount,updateTime
        OTCBeneficiarys otcBeneficiarys = otcBeneficiarysService.getOTCBeneficiarysByuserIdAndDictItemId(oldOrder.getSeller(), payMethod);
        if (otcBeneficiarys == null) {
            throw new IdaxException(OrderResponseEnum.PAYEE_NO_THISMETHOD.getRespCode());
        }

        order.setSelltype(payMethod);
        order.setSellaccount(otcBeneficiarys.getAccount());
        order.setOrderstate(OrderState.STATE_PAID.getCode());

        //更新订单状态为已买家已支付
        Boolean orderStatePaidToReleased = updateOrderStateCommon(order, oldOrder, userId);
        if (!orderStatePaidToReleased) {
            throw new IdaxException(OrderResponseEnum.ORDERSTATE_ERROR.getRespCode());
        }

        //orderlog
        if (otcOrderLogService.insertOtcOrderLog(oldOrder, OrderState.STATE_PAID.getCode()) <= 0) {
            throw new IdaxException(OrderResponseEnum.ORDERLOG_INSERTFAIL.getRespCode());
        }

        //发送消息
        pushOrderStateMessage(oldOrder, OrderState.STATE_PAID.getCode(), oldOrder.getBuyer());
        // 发送我的通知消息
        MessageNotifyUtils.sendNotify(oldOrder.getSeller(),
                oldOrder.getNumber(),
                MessageNotifyEnum.BodyTemplate.order_pay,
                MessageNotifyUtils.getNotifyNickName(userId));

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                List<SimpleUserLanguage> languages = userLanguageService.getBuyerAndSellerSimpleLanguage(oldOrder.getBuyer(), oldOrder.getSeller());

                //发送邮件通知
                //发给卖家  用户“张三” 已将订单001120181212070409000061s标记为“已付款”状态
                User sellerUser = userService.getUserById(oldOrder.getSeller());
                BaseEmaiDTO seller = new BaseEmaiDTO();
                String notifyNickName = MessageNotifyUtils.getNotifyNickName(userId);
                seller.setNickName(notifyNickName);
                seller.setOrderId(oldOrder.getNumber());
                sqsService.sendEmailMessage(CommonConstant.EmailType.IDENTIFICATION_PAYMENT_SELLER, sellerUser.getEmail(), languages.get(1).getLanguage(), oldOrder.getId(),
                        sqsQueueName, seller);

                //发给买家  您已将订单001120181212070409000061s标记为“已付款”状态
                User buyerUser = userService.getUserById(oldOrder.getBuyer());
                BaseEmaiDTO buyer = new BaseEmaiDTO();
                buyer.setNickName("您");
                buyer.setOrderId(oldOrder.getNumber());
                sqsService.sendEmailMessage(CommonConstant.EmailType.IDENTIFICATION_PAYMENT_BUYER, buyerUser.getEmail(), languages.get(0).getLanguage(), oldOrder.getId(),
                        sqsQueueName, buyer);

                //极光推送给卖家  买家{0}已经确认付款
                jiGuangService.pushTemplate(oldOrder, sellerUser.getEmail(), oldOrder.getSeller(), false, languages.get(1).getLanguage(), JiGuangPushEnum.ORDER_PAID, notifyNickName);

            }
        });
        return true;
    }

    /**
     * 取消订单
     *
     * @param oldOrder
     * @param userId
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {RuntimeException.class, Exception.class})
    public CapitalAPIParamsDTO cancelOrder(OTCOrder otcOrder, Integer userId) {


        //校验参数+insert requestLog
        CapitalAPIParamsDTO capitalAPIParamsDTO = otcOrderService.checkCancelOrderServiceParams(otcOrder, userId);
        LogUtils.info(this.getClass(), "cancelOrderCapitalAPIParamsDTO data is " + JSON.toJSONString(capitalAPIParamsDTO));

        //如果插入失敗 || 非第一次插入
        if (!capitalAPIParamsDTO.getInsert()) {
            throw new IdaxException(OrderResponseEnum.ORDERE_CANCEL_INSERT_FAIL.getRespCode());
        }

        OTCOrder updateOrder = new OTCOrder();
        updateOrder.setOrderstate(OrderState.STATE_BUYER_CANCEL.getCode());
        updateOrder.setCaneltime(DateUtils.getUTCNow());
        //修改订单状态
        if (!updateOrderStateCommon(updateOrder, otcOrder, otcOrder.getBuyer())) {
            throw new IdaxException(OrderResponseEnum.UPDATE_ORDERSTATEFAIL.getRespCode());
        }

        // 添加用户取消次数
        addCancelOrderNum(userId);

        //取消次数过3 需要冻结用户
        cancelOrderNumMoreThanThree(userId);

        //更新 orderlog
        if (otcOrderLogService.insertOtcOrderLog(otcOrder, OrderState.STATE_BUYER_CANCEL.getCode()) <= 0) {
            throw new IdaxException(OrderResponseEnum.ORDERLOG_INSERTFAIL.getRespCode());
        }

        //解冻账户 如果失败 交给补偿事务去做 返回true
        //如果之前已经插入过requestLog 查询这条记录补偿事务的状态
        capitalAPIBaseService.excludeCreateOrderRequestAPI(capitalAPIParamsDTO);


        return capitalAPIParamsDTO;

    }

    /**
     * 取消超过三次冻结用户
     *
     * @param userId
     */
    @Override
    public void cancelOrderNumMoreThanThree(Integer userId) {
        Long cancelOrderNum = getCancelOrderNum(userId);
        if (cancelOrderNum >= 3L) {
            userService.forzenUserStatus(userId);
        }
        LogUtils.info(this.getClass(), "cancelOrderNumMoreThanThree userid: " + userId + " ,cancelOrderNum: " + cancelOrderNum);
    }

    @Override
    public List<UserTradeInfo> getUserTwoMonthsAgoTradeInfo(List<Integer> allUpAdPublisherId) {
        String utcDateTwoMonthsAgo = DateUtils.getUtcDateTwoMonthsAgo();
        return otcOrderMapper.getUserTwoMonthsAgoTradeInfo(allUpAdPublisherId, utcDateTwoMonthsAgo);
    }


    /**
     * 60日订单数*0.35 + 60日订单完成率*0.45 + 60日成交的btc数量*0.2
     * 计算加权系数
     *
     * @param userTradeInfos
     */
    @Override
    public void calculationUserProportion(List<UserTradeInfo> userTradeInfos, List<Integer> calculationUserProportion) {
        Map<String, List<UserTradeInfo>> buyer =
                userTradeInfos.stream().collect(Collectors.groupingBy(u -> u.getBuyer().toString()));

        Map<String, List<UserTradeInfo>> seller =
                userTradeInfos.stream().collect(Collectors.groupingBy(u -> u.getSeller().toString()));

        BigDecimal initQty = BigDecimal.ZERO;
        Integer order = 0;
        Integer tradeOrder = 0;

        List<Integer> memoryUser = new ArrayList<>();

        if (!CollectionUtils.isEmpty(buyer) && !CollectionUtils.isEmpty(seller)) {
            for (Map.Entry<String, List<UserTradeInfo>> entry : buyer.entrySet()) {
                String userId = entry.getKey();

                if (calculationUserProportion.contains(Integer.valueOf(userId))) {
                    List<UserTradeInfo> buyerTradeInfoList = entry.getValue();

                    initQty = BigDecimal.ZERO;
                    order = buyerTradeInfoList.size();
                    tradeOrder = buyerTradeInfoList.stream().filter(p -> p.getTradeSuccess().equals(true)).collect(Collectors.toList()).size();

                    for (UserTradeInfo userTradeInfo : buyerTradeInfoList) {
                        initQty = initQty.add(getTradeCoinConvertBtcQty(userTradeInfo.getPairName(), userTradeInfo.getQty()));
                    }

                    List<UserTradeInfo> sellerTradeInfos = seller.get(userId);
                    if (!CollectionUtils.isEmpty(sellerTradeInfos)) {
                        order = order + sellerTradeInfos.size();
                        tradeOrder = tradeOrder + sellerTradeInfos.stream().filter(p -> p.getTradeSuccess().equals(true)).collect(Collectors.toList()).size();
                        for (UserTradeInfo sellerTradeInfo : sellerTradeInfos) {
                            initQty = initQty.add(getTradeCoinConvertBtcQty(sellerTradeInfo.getPairName(), sellerTradeInfo.getQty()));
                        }
                    }
                    BigDecimal proportion = new BigDecimal(order).multiply(orderTotal).add(new BigDecimal(tradeOrder).multiply(orderTradeRate)).add(initQty.multiply(orderTotalQty))
                            .setScale(8, BigDecimal.ROUND_DOWN);
                    ProportionDTO proportionDTO = new ProportionDTO(proportion, tradeOrder, new BigDecimal(tradeOrder).divide(new BigDecimal(order), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));
                    MemoryService.AD_PUBLISER_PROPORTION_MAP.put(Integer.valueOf(userId), proportionDTO);
                    memoryUser.add(Integer.valueOf(userId));
                }


            }
        } else if (!CollectionUtils.isEmpty(buyer) && CollectionUtils.isEmpty(seller)) {
            for (Map.Entry<String, List<UserTradeInfo>> entry : buyer.entrySet()) {
                String userId = entry.getKey();

                if (calculationUserProportion.contains(Integer.valueOf(userId))) {
                    List<UserTradeInfo> buyerTradeInfoList = entry.getValue();

                    initQty = BigDecimal.ZERO;
                    order = buyerTradeInfoList.size();
                    tradeOrder = buyerTradeInfoList.stream().filter(p -> p.getTradeSuccess().equals(true)).collect(Collectors.toList()).size();

                    for (UserTradeInfo userTradeInfo : buyerTradeInfoList) {
                        initQty = initQty.add(getTradeCoinConvertBtcQty(userTradeInfo.getPairName(), userTradeInfo.getQty()));
                    }
                    BigDecimal proportion = new BigDecimal(order).multiply(orderTotal).add(new BigDecimal(tradeOrder).multiply(orderTradeRate)).add(initQty.multiply(orderTotalQty))
                            .setScale(8, BigDecimal.ROUND_DOWN);
                    ProportionDTO proportionDTO = new ProportionDTO(proportion, tradeOrder, new BigDecimal(tradeOrder).divide(new BigDecimal(order), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));
                    MemoryService.AD_PUBLISER_PROPORTION_MAP.put(Integer.valueOf(userId), proportionDTO);
                    memoryUser.add(Integer.valueOf(userId));
                }

            }
        } else if (CollectionUtils.isEmpty(buyer) && !CollectionUtils.isEmpty(seller)) {
            for (Map.Entry<String, List<UserTradeInfo>> entry : seller.entrySet()) {
                String userId = entry.getKey();

                if (calculationUserProportion.contains(Integer.valueOf(userId))) {
                    List<UserTradeInfo> buyerTradeInfoList = entry.getValue();

                    initQty = BigDecimal.ZERO;
                    order = buyerTradeInfoList.size();
                    tradeOrder = buyerTradeInfoList.stream().filter(p -> p.getTradeSuccess().equals(true)).collect(Collectors.toList()).size();

                    for (UserTradeInfo userTradeInfo : buyerTradeInfoList) {
                        initQty = initQty.add(getTradeCoinConvertBtcQty(userTradeInfo.getPairName(), userTradeInfo.getQty()));
                    }
                    BigDecimal proportion = new BigDecimal(order).multiply(orderTotal).add(new BigDecimal(tradeOrder).multiply(orderTradeRate)).add(initQty.multiply(orderTotalQty))
                            .setScale(8, BigDecimal.ROUND_DOWN);
                    ProportionDTO proportionDTO = new ProportionDTO(proportion, tradeOrder, new BigDecimal(tradeOrder).divide(new BigDecimal(order), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")));
                    MemoryService.AD_PUBLISER_PROPORTION_MAP.put(Integer.valueOf(userId), proportionDTO);
                    memoryUser.add(Integer.valueOf(userId));
                }

            }
        } else if (calculationUserProportion.size() == 1) {
            MemoryService.AD_PUBLISER_PROPORTION_MAP.put(calculationUserProportion.get(0), new ProportionDTO());
        }

        //会有上述四种情况都没得。。 用户未有交易记录
        addLackUser(calculationUserProportion, memoryUser);

        LogUtils.info(this.getClass(), "memoryListProportion data" + JSON.toJSONString(MemoryService.AD_PUBLISER_PROPORTION_MAP));
    }

    private void addLackUser(List<Integer> calculationUserProportion, List<Integer> memoryUser) {

        List<Integer> lackUser = new ArrayList<>();
        HashSet hs1 = new HashSet(calculationUserProportion);
        HashSet hs2 = new HashSet(memoryUser);
        hs1.removeAll(hs2);
        lackUser.addAll(hs1);
        if (!lackUser.isEmpty()) {
            lackUser.stream().forEach(p -> MemoryService.AD_PUBLISER_PROPORTION_MAP.put(p, new ProportionDTO()));
        }
    }


    /**
     * 其他币兑btc的数量
     *
     * @param pairName
     * @param qty
     * @return
     */
    public BigDecimal getTradeCoinConvertBtcQty(String pairName, BigDecimal qty) {

        try {
            if (pairName.equals(BTC)) {
                return qty;
            } else if (pairName.equals(ETH)) {
                Object eth_btc = MemoryService.MARKETMAP.get("ETH_BTC");
                Market market = JSON.parseObject(eth_btc.toString(), Market.class);
                return qty.multiply(market.getLastPrice()).setScale(8, BigDecimal.ROUND_DOWN);
            } else if (pairName.equals(USDT)) {
                Object btc_usdt = MemoryService.MARKETMAP.get("BTC_USDT");
                Market market = JSON.parseObject(btc_usdt.toString(), Market.class);
                return qty.multiply(BigDecimal.ONE.divide(market.getLastPrice(), 8, RoundingMode.DOWN)).setScale(8, BigDecimal.ROUND_DOWN);
            } else if (pairName.equals(BITCNY)) {
                Market market = JSON.parseObject(MemoryService.MARKETMAP.get("BTC_USDT").toString(), Market.class);
                return qty.multiply(BigDecimal.ONE.divide(market.getLastPrice(), 8, RoundingMode.DOWN)
                        .multiply(new BigDecimal((String) MemoryService.SYSTEMSETTINGMAP.get(CommonConstant.OTC_BITCNY_LASTPRICE)))).setScale(8, RoundingMode.DOWN);
            }

            Market market = JSON.parseObject(MemoryService.MARKETMAP.get("BTC_USDT").toString(), Market.class);
            return qty.multiply(BigDecimal.ONE.divide(market.getLastPrice(), 8, RoundingMode.DOWN)
                    .multiply(new BigDecimal((String) MemoryService.SYSTEMSETTINGMAP.get(CommonConstant.OTC_CZZ_LASTPRICE)))).setScale(8, RoundingMode.DOWN);

        } catch (Exception e) {
            LogUtils.info(this.getClass(), "HashMarketPairIsEmpty pairName: " + pairName + " parseError: " + ExceptionUtils.getStackTrace(e));
            return BigDecimal.ZERO;
        }

    }


    @Override
    public OTCOrder getOTCOrderByUniqueKey(String uniqueKey) {

        OTCOrderExample example = new OTCOrderExample();
        example.createCriteria().andUniquekeyEqualTo(uniqueKey);
        List<OTCOrder> otcOrders = otcOrderMapper.selectByExample(example);
        return otcOrders.isEmpty() ? null : otcOrders.get(0);
    }

    @Override
    public Long getPlaceOrderQty() {
        String key = RedisPrefixKeyEnum.order_place_qty.getKey();
        return this.currentDayActionNum(key);
    }

    @Override
    public Long getCancelOrderNum(Integer loginUserId) {
        String key = RedisPrefixKeyEnum.order_cancel_num.getKey();
        key += loginUserId;
        String cacheValue = redisService.getCacheStrValue(key);
        cacheValue = StringUtils.isEmpty(cacheValue) ? BigDecimal.ZERO.toPlainString() : cacheValue;
        return new BigDecimal(cacheValue).longValue();
    }

    @Override
    public Long addCancelOrderNum(Integer loginUserId) {
        String key = RedisPrefixKeyEnum.order_cancel_num.getKey();
        key += loginUserId;
        return this.currentDayActionNum(key);
    }


    public Long currentDayActionNum(String key) {
        boolean hasKey = redisService.exitsKey(key);
        Long num = redisService.getIncr(key);
        if (!hasKey) {
            // 获取当前UTC日期
            Date currentDate = DateUtils.getUTCNow();
            // 获取最后过期日期
            Date expireDate = DateUtils.getDate(DateUtils.getDateToStr(currentDate, "yyyy-MM-dd'T'23:59:59"), CommonConstant.UTC_DATE_FORMAT_PATTREN);
            // 设置过期时间（允许存在误差）
            Long expireTime = (expireDate.getTime() - currentDate.getTime()) / 1000;
            redisService.setExpire(key, expireTime);
        }
        return num;
    }


    //买卖方未支付的订单笔数
    @Override
    public Integer getNumbereOfUnpaid(Integer buyer, Integer seller) {
        Integer numbereOfUnpaid = otcOrderMapper.getNumbereOfUnpaid(new NumbereOfUnpaid(buyer, seller));
        return numbereOfUnpaid;
    }

    @Override
    public OTCOrder selectByPrimaryKey(Integer orderId) {
        return this.otcOrderMapper.selectByPrimaryKey(orderId);
    }

    @Override
    public PageVo<TradeRecordVO> listTradeRecord(TradeRecordDTO tradeRecord) {
        if (StringUtils.isNotEmpty(tradeRecord.getUserUniqueKey())) {
            OTCUser otcUser = this.otcUserService.getOTCUserByUniqueKey(tradeRecord.getUserUniqueKey());
            if (otcUser != null) {
                tradeRecord.setUserId(otcUser.getUserid());
            }
        }


        List<TradeRecordVO> listTradeRecord = this.otcOrderMapper.listTradeRecordPage(tradeRecord);


        TradeRecordDTO queryTradeRecord = TradeRecordDTO.builder()
                .userId(tradeRecord.getUserId())
                .build();

        List<TradeRecordVO> countTradeRecord = this.otcOrderMapper.listTradeRecordPage(queryTradeRecord);

        listTradeRecord.stream().forEach(
                (p) -> {

                    if (p.getBuyer().equals(tradeRecord.getUserId())) {
                        p.setType(OrderEnum.TYPE_BUY.getCode());
                    } else {
                        p.setType(OrderEnum.TYPE_SELL.getCode());
                    }

                }
        );


        PageVo<TradeRecordVO> pageVo = PageVo.<TradeRecordVO>builder()
                .data(listTradeRecord)
                .pageIndex(tradeRecord.getPageIndex())
                .totalRecord(countTradeRecord.size())
                .build();
        return pageVo;
    }

    @Override
    public CapitalAPIParamsDTO checkCancelOrderServiceParams(OTCOrder otcOrder, Integer userId) {

        //当前的订单状态是否支持取消 1 +2 时支持 STATE_UNPAID
        if (otcOrder.getOrderstate().intValue() != OrderState.STATE_UNPAID.getCode().intValue()
                && otcOrder.getOrderstate().intValue() != OrderState.STATE_PAID.getCode().intValue()) {
            throw new IdaxException(OrderResponseEnum.ORDER_CANCELBY_ORDERSTATE.getRespCode());
        }

        //只有付钱的一方可以取消 卖单 买家出钱 买单 买家出钱
        if (otcOrder.getBuyer().intValue() != userId.intValue()) {
            throw new IdaxException(OrderResponseEnum.CANCEL_WITHNOAUTH.getRespCode());
        }

        //解冻之前先查询account
        Account account = accountService.getAccountByCoinIdAndUserId(otcOrder.getSeller(), otcOrder.getCoinid());
        if (account == null) {
            LogUtils.warn(this.getClass(), "checkCancelOrderServiceParams account is null,usreid: " + otcOrder.getSeller() + "coinid" + otcOrder.getCoinid());
            throw new IdaxException(OrderResponseEnum.ACCOUNT_ISNOTEXOST.getRespCode());
        }

        // 调用接口进行 下单之前冻结用户账户部分资金并作判断 可用-冻结 >=0 + 手续费

        ChangeAccountDTO changeAccountDTO = new ChangeAccountDTO();

        RequestLogSimple requestLogSimple = new RequestLogSimple();

        ChangeListData changeListData = this.makeChangeListData(account, otcOrder, AccountTransactionEnum.TRANSACTIONTYPE_CANCELORDER);
        //构造请求核心参数
        accountCapitalService.makeChangeAccountParams(changeListData, CapitalAPIEnum.CANCEL_ORDER, requestLogSimple, changeAccountDTO);

        //插入requestLog前先查
        CapitalAPIParamsDTO capitalAPIParamsDTO = CapitalAPIParamsDTO.builder()
                .guid(requestLogSimple.getGuid())
                .requestJson(requestLogSimple.getRequestJson())
                .reverseJson(requestLogSimple.getReverseJson())
                .capitalAPIEnum(CapitalAPIEnum.CANCEL_ORDER)
                .changeListData(changeListData)
                .changeAccountDTO(changeAccountDTO)
                .build();

        //insert requestLog
        RequestLog requestLog = capitalAPIBaseService.getRequestLog(capitalAPIParamsDTO.getCapitalAPIEnum().getTxId(), capitalAPIParamsDTO.getChangeListData().getSourceId());
        if (requestLog != null) {
            capitalAPIParamsDTO.setFirstInsert(false);
            capitalAPIParamsDTO.setInsert(true);
        } else {
            Boolean insert = capitalAPIBaseService.insertRequestLog(capitalAPIParamsDTO);
            capitalAPIParamsDTO.setFirstInsert(true);
            capitalAPIParamsDTO.setInsert(insert);
        }

        return capitalAPIParamsDTO;
    }

    /**
     * 过期订单
     *
     * @param otcOrder
     * @param userId
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {RuntimeException.class, Exception.class})
    public CapitalAPIParamsDTO checkForzenOrderServiceParams(OTCOrder otcOrder, Integer userId) throws Exception {

        Integer coinId = otcOrder.getCoinid();
        Integer seller = otcOrder.getSeller();

        //解冻之前先查询account 资金
        Account account = accountService.getAccountByCoinIdAndUserId(seller, coinId);
        if (account == null || otcOrder.getQty().compareTo(account.getFrozenamount()) > 0) {
            LogUtils.info(this.getClass(), "forzenOrderMethod account: " + JSON.toJSONString(account) + "orderInfo: " + JSON.toJSONString(otcOrder));
            throw new IdaxException(OrderResponseEnum.SELLER_ACCOUNT_NOT_ENOUGH.getRespCode());
        }


        // 更新订单状态为过期
        OTCOrder updateOrder = new OTCOrder();
        updateOrder.setOrderstate(OrderState.STATE_EXPIRE.getCode());
        updateOrder.setCaneltime(DateUtils.getUTCNow());
        if (!otcOrderService.updateOrderStateCommon(updateOrder, otcOrder, otcOrder.getBuyer())) {
            throw new IdaxException(OrderResponseEnum.TIMEOUTORDER_UNFROZEN_FAIL.getRespCode());
        }
        //更新 orderlog
        if (orderLogService.insertOtcOrderLog(otcOrder, OrderState.STATE_EXPIRE.getCode()) <= 0) {
            throw new IdaxException(OrderResponseEnum.ORDERLOG_INSERTFAIL.getRespCode());
        }

        ChangeListData changeListData = ChangeListData.builder()
                .accountId(account.getId())
                .userId(otcOrder.getSeller())
                .coinId(otcOrder.getCoinid())
                .coinCode(otcOrder.getPairname())
                .transactionType(AccountTransactionEnum.TRANSACTIONTYPE_CANCELORDER.getCode())
                .remark("otc_timeout_order")
                .sourceId(otcOrder.getId())
                .availableChange(otcOrder.getQty())
                .frozenChange(otcOrder.getQty().multiply(new BigDecimal("-1")))
                .build();

        RequestLogSimple requestLogSimple = new RequestLogSimple();

        ChangeAccountDTO changeAccountDTO = new ChangeAccountDTO();

        //构造请求核心参数
        accountCapitalService.makeChangeAccountParams(changeListData, CapitalAPIEnum.OVERDUE_ORDER, requestLogSimple, changeAccountDTO);

        CapitalAPIParamsDTO capitalAPIParamsDTO = CapitalAPIParamsDTO.builder()
                .guid(requestLogSimple.getGuid())
                .requestJson(requestLogSimple.getRequestJson())
                .reverseJson(requestLogSimple.getReverseJson())
                .capitalAPIEnum(CapitalAPIEnum.OVERDUE_ORDER)
                .changeListData(changeListData)
                .changeAccountDTO(changeAccountDTO)
                .build();

        //insert requestLog
        RequestLog requestLog = capitalAPIBaseService.getRequestLog(capitalAPIParamsDTO.getCapitalAPIEnum().getTxId(), capitalAPIParamsDTO.getChangeListData().getSourceId());
        if (requestLog != null) {
            capitalAPIParamsDTO.setFirstInsert(false);
            capitalAPIParamsDTO.setInsert(true);
        } else {
            Boolean insert = capitalAPIBaseService.insertRequestLog(capitalAPIParamsDTO);
            capitalAPIParamsDTO.setFirstInsert(true);
            capitalAPIParamsDTO.setInsert(insert);
        }
        accountService.unforzenUserAccountPayTimeOut(otcOrder, capitalAPIParamsDTO);
        return capitalAPIParamsDTO;

    }

    /**
     * 放行订单参数校验
     *
     * @param otcOrder
     * @param userId
     * @return
     */
    @Override
    public CapitalAPIParamsDTO checkReleaseOrderServiceParams(OTCOrder otcOrder, Integer userId) {

        //获取广告信息
        OTCAds otcAds = otcAdsService.selectByPrimaryKey(otcOrder.getAdid());
        if (otcAds == null) {
            LogUtils.warn(this.getClass(), "checkReleaseOrderServiceParams ,otcAd not exist,adid: " + otcOrder.getAdid());
            throw new IdaxException(OTCAdResponseEnum.DATA_NOT_EXIST.getCode());
        }

        //卖家才可以放行
        if (otcOrder.getSeller().intValue() != userId.intValue()) {
            throw new IdaxException(OrderResponseEnum.ORDER_PAYAUTHSELLER.getRespCode());
        }

        // 1 考虑其他状态，如正在仲裁中是否可放行
        if (otcOrder.getOrderstate().intValue() != OrderState.STATE_PAID.getCode().intValue()
                && otcOrder.getOrderstate().intValue() != OrderState.STATE_TORELEASED.getCode().intValue()
                && otcOrder.getOrderstate().intValue() != OrderState.STATE_CANCEL_ARBITRATION.getCode().intValue()) {
            LogUtils.warn(this.getClass(), "releasOrderStatus error,orderNum: " + otcOrder.getNumber() + ",orderstate: " + otcOrder.getOrderstate() + ",ordersubstate: " + otcOrder.getOrdersubstate());
            throw new IdaxException(OrderResponseEnum.ORDERSTATE_ERROR.getRespCode());
        }


        RequestLogSimple requestLogSimple = new RequestLogSimple();
        ChangeListData changeListData = new ChangeListData();
        changeListData.setSourceId(otcOrder.getId());
        //OTCOrder order,CapitalAPIEnum changeAccountEnum,long guid, String requestJson,String reverseJson
        //构造请求核心参数
        requestLogSimple = accountCapitalService.makeTransferCapitalData(otcOrder, CapitalAPIEnum.COMPLETE_ORDER, requestLogSimple);

        CapitalAPIParamsDTO capitalAPIParamsDTO = CapitalAPIParamsDTO.builder()
                .guid(requestLogSimple.getGuid())
                .requestJson(requestLogSimple.getRequestJson())
                .reverseJson(requestLogSimple.getReverseJson())
                .capitalAPIEnum(CapitalAPIEnum.COMPLETE_ORDER)
                .changeListData(changeListData)
                .changeAccountDTO(new ChangeAccountDTO())
                .build();

        //insert requestLog
        RequestLog requestLog = capitalAPIBaseService.getRequestLog(capitalAPIParamsDTO.getCapitalAPIEnum().getTxId(), capitalAPIParamsDTO.getChangeListData().getSourceId());

        capitalAPIParamsDTO.setFirstInsert(false);

        if (requestLog == null) {
            capitalAPIBaseService.insertRequestLog(capitalAPIParamsDTO);
            capitalAPIParamsDTO.setFirstInsert(true);
        }

        return capitalAPIParamsDTO;
    }

    @Override
    public ChangeListData makeChangeListData(Account account, OTCOrder otcOrder, AccountTransactionEnum accountTransactionEnum) {
        ChangeListData changeListData = ChangeListData.builder()
                .accountId(account.getId())
                .userId(otcOrder.getSeller())
                .coinId(otcOrder.getCoinid())
                .coinCode(otcOrder.getPairname())
                .transactionType(AccountTransactionEnum.TRANSACTIONTYPE_CANCELORDER.getCode())
                .remark(AccountTransactionEnum.TRANSACTIONTYPE_CANCELORDER.getRemark())
                .sourceId(otcOrder.getId())
                .availableChange(otcOrder.getQty())
                .frozenChange(otcOrder.getQty().multiply(new BigDecimal("-1")))
                .build();
        return changeListData;
    }

    @Override
    public List<NeedAutoCommentDTO> getNeedAutoCommentOrder() {
        String time = DateUtils.getUtcDateSevenDayAgo();
        return otcOrderMapper.getNeedAutoCommentOrder(time);
    }

    public Integer getOrderId() {
        if (!redisTemplate.hasKey(CommonConstant.REDIS_ORDER_ID_KEY)) {
            OTCOrder lastOrder = otcOrderMapper.getLastOrder();
            if (null != lastOrder) {
                redisTemplate.opsForValue().set(CommonConstant.REDIS_ORDER_ID_KEY, lastOrder.getId().toString());
            }
        }
        return Math.toIntExact(redisTemplate.opsForValue().increment(CommonConstant.REDIS_ORDER_ID_KEY, 1));
    }
}